from app.Models.FinanceModels.Expense import Expense  # 按实际路径调整
from datetime import date
from sqlalchemy import func
from app.Models.FinanceModels.ExpenseFirstCate import ExpenseFirstCate
from app.Models.FinanceModels.ExpenseSecondCate import ExpenseSecondCate
from app.db import db  # 从独立的 db 模块导入
import pandas as pd


class ExpenseDAO:
    """支出模型的数据访问对象"""

    @staticmethod
    def create_expense(user_id, first_cate_id, second_cate_id, amount, expense_date, note=None, attach_path=None):
        """
        新增支出记录
        :param user_id: 用户ID
        :param first_cate_id: 一级分类ID
        :param second_cate_id: 二级分类ID
        :param amount: 支出金额
        :param expense_date: 支出日期（date类型）
        :param note: 支出说明（可选）
        :param attach_path: 附件路径（可选）
        :return: 新增的支出记录对象
        """
        new_expense = Expense(
            user_id=user_id,
            first_cate_id=first_cate_id,
            second_cate_id=second_cate_id,
            amount=amount,
            date=expense_date,
            note=note.strip() if note else None,
            attach_path=attach_path
        )
        try:
            db.session.add(new_expense)
            db.session.commit()
            return True, 0
        except Exception as e:
            return False, e

    @staticmethod
    def get_expense_by_id(expense_id):
        """
        根据ID查询支出记录
        :param expense_id: 支出记录ID
        :return: 支出记录对象或None
        """
        return Expense.query.get(expense_id)

    @staticmethod
    def get_expenses_by_user(user_id, start_date=None, end_date=None):
        """
        查询指定用户的支出记录，支持日期范围过滤
        :param user_id: 用户ID
        :param start_date: 开始日期（date类型，可选）
        :param end_date: 结束日期（date类型，可选）
        :return: 支出记录列表
        """
        query = Expense.query.filter_by(user_id=user_id)

        # 日期范围过滤
        if start_date:
            query = query.filter(Expense.date >= start_date)
        if end_date:
            query = query.filter(Expense.date <= end_date)

        # 按日期倒序排列（最新记录在前）
        return query.order_by(Expense.date.desc()).all()

    @staticmethod
    def get_expenses_with_categories(user_id, first_cate_id=None, second_cate_id=None):
        """
        关联查询支出记录及对应的一、二级分类名称
        :param user_id: 用户ID
        :param first_cate_id: 一级分类ID（可选）
        :param second_cate_id: 二级分类ID（可选）
        :return: 包含分类名称的支出记录字典列表
        """
        # 关联查询：支出表 + 一级分类表 + 二级分类表
        query = db.session.query(
            Expense,
            ExpenseFirstCate.first_cate_name.label('first_cate_name'),
            ExpenseSecondCate.second_cate_name.label('second_cate_name')
        ).join(
            ExpenseFirstCate,
            Expense.first_cate_id == ExpenseFirstCate.id,
            isouter=True  # 左连接，允许分类ID为空
        ).join(
            ExpenseSecondCate,
            Expense.second_cate_id == ExpenseSecondCate.id,
            isouter=True
        ).filter(
            Expense.user_id == user_id
        )

        # 条件过滤
        if first_cate_id:
            query = query.filter(Expense.first_cate_id == first_cate_id)
        if second_cate_id:
            query = query.filter(Expense.second_cate_id == second_cate_id)

        # 执行查询并按日期降序排序
        results = query.order_by(Expense.date.desc()).all()

        # 转换为字典列表（包含分类名称）
        expense_dicts = []
        for expense, first_name, second_name in results:
            exp_dict = expense.to_dict()
            exp_dict['first_cate_name'] = first_name
            exp_dict['second_cate_name'] = second_name
            expense_dicts.append(exp_dict)
        expense_df = pd.DataFrame(expense_dicts)

        return expense_df

    @staticmethod
    def get_expenses_by_second_cate(second_cate_id):
        """
        查询指定二级分类下的所有支出记录（用于分类删除前的关联校验）
        :param second_cate_id: 二级分类ID
        :return: 支出记录列表
        """
        return Expense.query.filter_by(second_cate_id=second_cate_id).all()

    @staticmethod
    def update_expense(expense_id, **kwargs):
        """
        更新支出记录
        :param expense_id: 支出记录ID
        :param kwargs: 待更新的字段（如amount, date, note等）
        :return: 更新后的支出记录对象或None
        """
        expense = ExpenseDAO.get_expense_by_id(expense_id)
        if not expense:
            print(expense_id, "不存在")
            return None

        # 批量更新字段
        for key, value in kwargs.items():
            if hasattr(expense, key):
                # 特殊处理字符串字段（去除首尾空格）
                if key == "note" and value is not None:
                    value = value.strip()
                setattr(expense, key, value)

        db.session.commit()
        return expense

    @staticmethod
    def delete_expense(expense_id):
        """
        删除支出记录
        :param expense_id: 支出记录ID
        :return: 成功返回True，失败返回False
        """
        expense = ExpenseDAO.get_expense_by_id(expense_id)
        if not expense:
            return False

        # 如果有附件，可在此处添加删除物理文件的逻辑
        # 例如：if expense.attach_path: os.remove(expense.attach_path)

        result = db.session.delete(expense)
        db.session.commit()

        return True

    @staticmethod
    def get_expense_summary(user_id, year, month=None):
        """
        统计用户的支出总和（按年或按月）
        :param user_id: 用户ID
        :param year: 年份
        :param month: 月份（可选）
        :return: 支出总金额（浮点数）
        """
        query = Expense.query.filter_by(user_id=user_id)

        # 过滤年份
        query = query.filter(func.extract('year', Expense.date) == year)

        # 过滤月份（如果指定）
        if month:
            query = query.filter(func.extract('month', Expense.date) == month)

        # 计算总和
        result = query.with_entities(func.sum(Expense.amount)).first()
        return float(result[0]) if result[0] else 0.0

    @staticmethod
    def get_monthly_expense_trend(user_id, year):
        """
        获取用户全年每月支出趋势
        :param user_id: 用户ID
        :param year: 年份
        :return: 包含1-12月支出的列表
        """
        # 按月分组统计
        monthly_data = db.session.query(
            func.extract('month', Expense.date).label('month'),
            func.sum(Expense.amount).label('total')
        ).filter(
            Expense.user_id == user_id,
            func.extract('year', Expense.date) == year
        ).group_by('month').order_by('month').all()

        # 填充1-12月数据（确保每个月都有记录，无数据则为0）
        trend = [0.0] * 12
        for month, total in monthly_data:
            trend[int(month) - 1] = float(total) if total else 0.0
        return trend

    # 所有的一级分类
    @staticmethod
    def get_all_first_cates():
        """
        获取所有一级分类
        :return:
        """
        categories = ExpenseFirstCate.query.all()
        # 利用模型自带的to_dict()方法转换为字典列表
        cate_data = [cate.to_dict() for cate in categories]

        # 转换为DataFrame
        df = pd.DataFrame(cate_data)
        return df

    @staticmethod
    def get_second_cates_by_first_id(first_cate_id):
        """
        根据一级分类ID查询所有可选的二级分类
        :param first_cate_id: 一级分类ID
        :return: 二级分类的DataFrame，包含id和分类名称等信息
        """
        # 查询指定一级分类下的所有二级分类（假设二级分类模型有first_cate_id字段关联一级分类）
        second_cates = ExpenseSecondCate.query.filter_by(
            first_cate_id=first_cate_id
        ).order_by(ExpenseSecondCate.id).all()

        # 转换为字典列表
        cate_data = [cate.to_dict() for cate in second_cates]

        # 转换为DataFrame返回
        return pd.DataFrame(cate_data)
